// JavaScript Document

var diceForm = document.getElementById('diceForm');
var diceFormText = diceForm.elements[0];
var diceFormButton = diceForm.elements[1];
var diceCollectionArray = new Array();
var localMap;
var expressionHistory = [""];
var expressionHistoryCurrentIndex = 0;
var latestUpdate = 0;
var playerPersonnage;
var personnageList = new listePersonnages();


gapi.hangout.onApiReady.add(function(eventObj) {
  if (eventObj.isApiReady) {
	diceFormText.focus();
    alert("Chargement de l'API terminé.386");
	localMap = new Map(16, 16);
	localMap.initialize();
	update(null);
	gapi.hangout.data.onStateChanged.add(update);
	
	diceForm.addEventListener('submit', function(e) {
		REGEXThrow(diceFormText.value);
		e.preventDefault();
		diceFormText.value = "";
	}, true);
	
	diceForm.addEventListener('keyup', function(e) {
		if(e.which == 38){
			diceFormText.value = getExpressionHistoryPrevious();
			diceFormText.focus();
			diceFormText.select();
		}
		else if(e.which == 40){
			diceFormText.value = getExpressionHistoryNext();
			diceFormText.focus();
			diceFormText.select();
		}
	}, true);
	document.getElementById('floorTool').addEventListener('click', function(e) {
			localMap.currentAttribute = 'floor';
			localMap.currentAttributeValue = 'true';
	}, true);
	document.getElementById('unFloorTool').addEventListener('click', function(e) {
			localMap.currentAttribute = 'floor';
			localMap.currentAttributeValue = 'false';
	}, true);
	document.getElementById('selectAllTiles').addEventListener('click', function(e) {
			localMap.currentSelectionFunction = localMap.getAllTiles;
	}, true);
	document.getElementById('selectInsideTiles').addEventListener('click', function(e) {
			localMap.currentSelectionFunction = localMap.getOnlyInnerTiles;
	}, true);
	document.getElementById('moveTool').addEventListener('click', function(e) {
			localMap.currentSelectionFunction = localMap.getOnlyInnerTiles;
	}, true);
	document.getElementById('pin').addEventListener('click', function(e) {
		if(document.getElementById('diceFormContainer').getAttribute('locked') == 'true'){
			document.getElementById('diceFormContainer').setAttribute('locked', 'false');
			document.getElementById('pin').setAttribute('src', 'http://rpg-hangout.googlecode.com/svn/trunk/img/unlocked.png');
		}
		else{
			document.getElementById('diceFormContainer').setAttribute('locked', 'true');
			document.getElementById('pin').setAttribute('src', 'http://rpg-hangout.googlecode.com/svn/trunk/img/lock.png');
		}
	}, true);
	playerPersonnage = new Personnage();
	playerPersonnage.initialize();
	playerPersonnage.moveToTile(localMap.getTile(1, 1));
  }
});


function getExpressionHistoryPrevious(){
	if(expressionHistoryCurrentIndex == 0){
		return expressionHistory[0];
	}
	else{
		return expressionHistory[--expressionHistoryCurrentIndex]
	}
}
function getExpressionHistoryNext(){
	if(expressionHistoryCurrentIndex == expressionHistory.length - 2){
		expressionHistoryCurrentIndex++;
		return "";
	}
	else if(expressionHistoryCurrentIndex == expressionHistory.length - 1){
		return "";
	}
	else{
		return expressionHistory[++expressionHistoryCurrentIndex]
	}
}

function sortMetadataByTimestamp(a, b){
	return(a.timestamp - b.timestamp);
}

function getLocalPlayer(){
	return gapi.hangout.getParticipantById(gapi.hangout.getParticipantId());
}

function REGEXThrow(inputText){
	var numberOfDice=0;
	var faces = 0;
	var mod = 0;
	if(/^([0-9]+)[dD]([0-9]+)$/.test(inputText)){
		/^([0-9]+)[dD]([0-9]+)$/.exec(inputText);
		numberOfDice = parseInt(RegExp.$1, 10);
		faces = parseInt(RegExp.$2, 10);
	}
	else if(/^([0-9]+)[dD]([0-9]+)([+-][0-9]+)$/.test(inputText)){
		/^([0-9]+)[dD]([0-9]+)([+-][0-9]+)$/.exec(inputText);
		numberOfDice = parseInt(RegExp.$1, 10);
		faces = parseInt(RegExp.$2, 10);
		mod = parseInt(RegExp.$3, 10);
	}
	else{
		alert("valeur invalide");
		return null;
	}
	if(numberOfDice > 50 || numberOfDice <= 0 || faces <=0 || (faces > 20 && faces != 100)){
		if(numberOfDice > 50){
			alert("Too many dice ! (max : 50)");
		}
		if(numberOfDice <= 0){
			alert("not enough dice ! (min : 1)");
		}
		if(faces <=0){
			alert("The dice must have at least one face ! (min : 1)");
		}
		if(faces > 50){
			alert("The dice must have at most 20 faces (exception : one hundred faces) ! (max : 20, except 100)");
		}
		return null;
	}
	expressionHistory[expressionHistory.length-1] = inputText;
	expressionHistory.push("");
	expressionHistoryCurrentIndex = expressionHistory.length-1;
	(new DiceThrowCollection(numberOfDice, faces, mod)).throwDice();
}

function update(eventObj){
	var stateCopy = gapi.hangout.data.getStateMetadata();
	var sortedState = new Array();
	for(var myKey in stateCopy){
		sortedState.push(stateCopy[myKey]);
	}
	sortedState.sort(sortMetadataByTimestamp);
	var length = sortedState.length;
	for(var i = 0 ; i < length; i++){
		if(sortedState[i].timestamp > latestUpdate){
			handleUpdate(sortedState[i]);
		}
	}
}

function handleUpdate(updateObject){
	if(/^DiceCollection.*$/.test(updateObject.key)){
		handleDiceCollectionUpdate(updateObject);
	}
	else if(/^TileUpdate.*$/.test(updateObject.key)){
		handleTileUpdate(updateObject);
	}
	else if(/^PersonnageUpdate.*$/.test(updateObject.key)){
		handlePersonnageUpdate(updateObject);
	}
	latestUpdate = updateObject.timestamp;
}

function handleDiceCollectionUpdate(updateObject){
	var parsedJss = JSON.parse(updateObject.value);
	var newCollection = new DiceThrowCollection(parsedJss.numberOfDice, parsedJss.faces, parsedJss.mod);
	newCollection.cloneFromParsedObject(parsedJss);
	newCollection.insertOnTop();
	return newCollection;
}

function handleTileUpdate(updateObject){
	var parsedJSS = JSON.parse(updateObject.value);
	localMap.getTile(parsedJSS.i, parsedJSS.j).update(parsedJSS);
}

function handlePersonnageUpdate(updateObject){
	var parsedJSS = JSON.parse(updateObject.value);
	personnageList.update(parsedJSS);
}

function filterDetailedResultsByRowId(rowId, threshold){
	diceCollectionArray[rowId].filterDiceResults(threshold);
}

function cancelFilterDetailedResultsByRowId(rowId){
	diceCollectionArray[rowId].cancelFilterDiceResults();
	}

function listePersonnages(){
	this.personnages = new Array();
	
	this.getPersonnageByTile = function(tile){
		for(var personnage in this.personnages){
			if(this.personnages[personnage].attributes.i == tile.i && this.personnages[personnage].attributes.j == tile.j){
				return this.personnages[personnage];
			}
		}
		return null;
	}
	
	this.getPersonnageById = function(id){
		for(var personnage in this.personnages){
			if(this.personnages[personnage].attributes.id == id){
				return this.personnages[personnage];
			}
		}
		return null;
	}
	
	this.addPersonnage = function(perso){
		this.personnages.push(perso);
	}
	
	this.update = function(updateObject){
		var oldPerso = this.getPersonnageById(updateObject.attributes.id);
		if(oldPerso == null){
			oldPerso = new Personnage();
			oldPerso.createLocalAttributes();
			this.addPersonnage(oldPerso);
		}
		oldPerso.update(udpateObject);
	}
}

function Personnage(){
	this.attributes = {
		i: null,
		j: null,
		initialized: false,
		playerImageUrl: null,
		id:0
	};
	
	this.localAttributes = {
		imgNode:null
	}
	
	this.createLocalAttributes = function(){
		this.localAttributes.imgNode = document.createElement('img');
	}
	
	this.setPictureSrc = function(){
		this.localAttributes.imgNode.src = this.attributes.playerImageUrl;
	}
	
	this.getCurrentTile = function(){
		if(this.attributes.i == null || this.attributes.j == null){
			return null;
		}
		return localMap.getTile(this.attributes.i, this.attributes.j);
	}
	
	this.moveToTile = function(tile){
		if(this.attributes.i != null && this.attributes.j != null)
			this.getCurrentTile().removeBackgroundImage(this.attributes.imgNode);
		this.attributes.i = tile.i;
		this.attributes.j = tile.j;
		this.getCurrentTile().setBackgroundImage(this.attributes.imgNode);
	}
	
	this.commit = function(updateObject){
		gapi.hangout.data.setValue( 'PersonnageUpdate:' + this.attributes.id, JSON.stringify(this) );
	}
	
	this.update = function(updateObject){
		this.updateAttributes(updateObject);
		this.moveToTile(localMap.getTile(this.getCurrentTile()));
	}
	
	this.updateAttributes = function(updateObject){
		for(var attribut in updateObject.attributes){
			this.attributes[attribut] = updateObject.attributes[attribut];
		}
	}
}

function DiceThrowCollection(numberOfDice, faces, mod){
	this.playerImageUrl = getLocalPlayer().person.image.url;
	this.rowId = null;
	this.numberOfDice = numberOfDice;
	this.faces = faces;
	this.mod = mod;
	this.requestString = "";
	this.score = 0;
	this.total = 0;
	this.isThrownYet = false;
	this.initialized = false;
	this.comesFromSharedState = false;
	this.diceArray = new Array();
	
	this.initialize = function(){
		if(this.initialized){
			return null;
		}
		this.numberOfDice = parseInt(this.numberOfDice, 10);
		this.mod = parseInt(this.mod, 10);
		this.faces = parseInt(this.faces, 10);
		for(var i = 0 ; i < this.numberOfDice; i++){
			this.diceArray.push(new DiceThrow(this.faces, this.mod));
		}
		this.initialized = true;
	}
	
	this.throwDice = function(){
		if(! this.initialized){
			this.initialize();
		}
		
		for(var i = 0 ; i < this.numberOfDice; i++){
			this.diceArray[i].throwDice();
			this.score += this.diceArray[i].score;
			this.total += this.diceArray[i].total;
		}
		this.requestString = this.numberOfDice + "d" + this.faces;
		if(this.mod < 0){
			this.requestString += " - " + (-1 * this.mod);
		}
		else if(this.mod > 0){
			this.requestString += " + " + this.mod;
		}
		this.isThrownYet = true;
		this.commit();
	}
	
	this.commit = function(){
		if(! this.comesFromSharedState ){
			this.rowId = "DiceCollection" + Math.random();
			gapi.hangout.data.setValue( this.rowId , JSON.stringify(this) );
		}
	}
	
	this.cloneFromParsedObject = function(obj){
		if(! this.initialized ){
			this.initialize();
		}
		this.playerImageUrl = obj.playerImageUrl;
		this.rowId = obj.rowId;
		this.numberOfDice = obj.numberOfDice;
		this.faces = obj.faces;
		this.requestString = obj.requestString;
		this.score = obj.score;
		this.total = obj.total;
		this.isThrownYet = true;
		this.comesFromSharedState = true;
		for(var i=0; i < this.numberOfDice; i++){
			this.diceArray[i].cloneFromParsedObject(obj.diceArray[i]);
		}
		diceCollectionArray[this.rowId] = this;
	}
	
	this.getRow = function(){
		return document.getElementById(this.rowId);
	}
	
	this.getDiceArrayOfThrowsHigherThan = function(threshold){
		var result = new Array();
		for(var i = 0; i < this.numberOfDice; i++){
			if(this.diceArray[i].total >= threshold){
				result.push(this.diceArray[i]);
			}
		}
		return result;
	}
	
	this.GetDiceTotalString = function(){
		if(! this.isThrownYet ){
			this.throwDice();
		}
		
		var maxScore = this.faces * this.numberOfDice;
		var scoreQuality="";
		if(this.score <= (maxScore/4)){
			scoreQuality="low";
		}
		else if(this.score <= ((3*maxScore)/4)){
			scoreQuality = "medium";
		}
		else{
			scoreQuality = "high";
		}
		var totalRequestString = this.numberOfDice + "d" + this.faces;
		if(this.mod >= 0){
			totalRequestString += " + " + (this.numberOfDice * this.mod);
		}
		else{
			totalRequestString += " - " + (-1 * (this.numberOfDice * this.mod));
		}
		var resultString = "<span score=\""+scoreQuality+"\"><abbr title=\""+ totalRequestString +"\">"+this.total+"</abbr></span>";
		return resultString;
	}
	
	this.getDetailedResultsString = function(dArray){
		if(! this.isThrownYet ){
			this.throwDice();
		}
		if(dArray == null){
			dArray = this.diceArray;
		}
		else if(dArray.length == 0){
			return "No results";
		}
		var resultsString = "";
		var i;
		for(i = 0; i < (dArray.length - 1); i++){
			resultsString += dArray[i].toString(this.rowId) + " + ";
		}
		resultsString += dArray[i].toString(this.rowId);
		return resultsString;
	}
	
	this.filterDiceResults = function(threshold){
		threshold = parseInt(threshold, 10);
		var filteredDiceArray = this.getDiceArrayOfThrowsHigherThan(threshold);
		var filteredResultsString = this.getDetailedResultsString(filteredDiceArray);
		this.getRow().cells[3].innerHTML = "<span class=\"diceResultsFilter\" onclick=\"cancelFilterDetailedResultsByRowId('" + this.rowId + "');\"><abbr title=\"Seuls les résultats supérieurs ou égaux à " + threshold + " sont affichés. Cliquez sur ce filtre pour le retirer.\"> ≥ " + threshold + "</abbr></span> " + filteredResultsString;
	}
	
	this.cancelFilterDiceResults = function(){
		this.getRow().cells[3].innerHTML = this.getDetailedResultsString(null);
	}
	
	this.insertOnTop = function(){
		if(! this.isThrownYet ){
			this.throwDice();
		}
		var resultsTable = document.getElementById("diceResults");
		resultsTable.insertRow(0);
		resultsTable.rows[0].setAttribute("id", this.rowId);
		
		
		resultsTable.rows[0].insertCell(0);
		resultsTable.rows[0].cells[0].innerHTML = "<img class=\"playerImage\" src=\"" + this.playerImageUrl + "\"/>";
		
		resultsTable.rows[0].insertCell(-1);
		resultsTable.rows[0].cells[1].innerHTML = this.GetDiceTotalString();
		
		resultsTable.rows[0].insertCell(-1);
		resultsTable.rows[0].cells[2].innerHTML = "<abbr title=\"max:" + this.numberOfDice*this.faces + "\">(" + this.requestString + "</abbr>)";
		
		resultsTable.rows[0].insertCell(-1);
		resultsTable.rows[0].cells[3].innerHTML = this.getDetailedResultsString(null);
	}
}

function DiceThrow(faces, mod){
	this.faces = faces;
	this.mod = mod;
	this.score = 0;
	this.total = 0;
	this.isThrownYet = false;
	this.requestString="";
	
	this.cloneFromParsedObject = function(obj){
		this.faces = obj.faces;
		this.mod = obj.mod;
		this.score = obj.score;
		this.total = obj.total;
		this.isThrownYet = true;
		this.requestString = obj.requestString;
	}
	
	this.throwDice = function(){
		this.score =  Math.ceil(Math.random()*faces);
		this.total = this.score + this.mod;
		this.requestString = this.score;
		if(this.mod >= 0){
			this.requestString += " + " + this.mod;
		}
		else{
			this.requestString += " - " + (-1 * this.mod);
		}
		this.isThrownYet = true;
	}
	
	this.toString = function(rowId){
		if(! this.isThrownYet ){
			this.throwDice();
		}
		
		var scoreQuality="";
		if(this.score <= (faces/4)){
			scoreQuality="low";
		}
		else if(this.score <= ((3*faces)/4)){
			scoreQuality = "medium";
		}
		else{
			scoreQuality = "high";
		}
		var resultString = "<span onclick=\"filterDetailedResultsByRowId('" + rowId + "', " + this.total + ");\" score=\"" + scoreQuality + "\"><abbr title=\"" + this.requestString +"\">"+this.total+"</abbr></span>";
		return resultString;
	}
}

function Map(width, height){
	this.width = width;
	this.height = height;
	this.lines = new Array();
	this.mouseDownTile = null;
	this.mouseUpTile = null;
	
	this.currentFunction = null;
	this.currentAttribute = "floor";
	this.currentAttributeValue = "true";
	this.currentSelectionFunction = null;
	
	this.initialize = function(){
		var accuLine;
		for(var i = 0; i < this.height; i++){
			accuLine = new Array();
			this.getMapHTMLObject().insertRow(-1);
			for(var j = 0; j < this.width; j++){
				accuLine.push(new MapTile(i, j, this));
				this.getMapHTMLObject().rows[i].insertCell(-1);
				accuLine[j].initialize();
			}
			this.lines.push(accuLine);
		}
		this.currentFunction = this.turnAttribute;
		this.currentSelectionFunction = this.getAllTiles;
	}
	
	this.getMapHTMLObject = function(){
		return document.getElementById('map');
	}
	this.getTileHTMLObject = function(i, j){
		return this.getMapHTMLObject().rows[i].cells[j];
	}
	
	this.setMouseDownTile = function(i, j){
		this.mouseDownTile = this.getTile(i, j);
	}
	this.setMouseUpTile = function(i, j){
		this.mouseUpTile = this.getTile(i, j);
		if(personnageList.getPersonnageByTile(this.mouseDownTile) != null){
			var selectedPersonnage = personnageList.getPersonnageByTile(this.mouseDownTile);
			selectedPersonnage.moveToTile(this.mouseUpTile);
			return null;
		}
		this.applyCurrentFunctionToTileArray(this.currentSelectionFunction(this.mouseDownTile, this.mouseUpTile));
	}
	
	this.turnAttribute = function(tile){
		tile.setAttribute(this.currentAttribute, this.currentAttributeValue);
	}
	
	this.applyCurrentFunctionToTileArray = function(tArray){
		var length = tArray.length
		for(var i = 0; i<length; i++){
			this.currentFunction(tArray[i]);
			tArray[i].commit();
		}
	}
	
	this.getOnlyInnerTiles = function(tile1, tile2){
		var minI = Math.min(tile1.i, tile2.i);
		var minJ = Math.min(tile1.j, tile2.j);
		var maxI = Math.max(tile1.i, tile2.i);
		var maxJ = Math.max(tile1.j, tile2.j);
		var line;
		var col;
		var result = new Array();
		for(line = minI+1; line < maxI; line++){
			for(col = minJ+1; col < maxJ; col++){
				result.push(this.getTile(line, col));
			}
		}
		return result;
	}
	
	this.getAllTiles = function(tile1, tile2){
		var minI = Math.min(tile1.i, tile2.i);
		var minJ = Math.min(tile1.j, tile2.j);
		var maxI = Math.max(tile1.i, tile2.i);
		var maxJ = Math.max(tile1.j, tile2.j);
		var line;
		var col;
		var result = new Array();
		for(line = minI; line <= maxI; line++){
			for(col = minJ; col <= maxJ; col++){
				result.push(this.getTile(line, col));
			}
		}
		return result;
	}
	
	this.getClickedTilesArray = function(tile1, tile2){
		return new Array(tile1, tile2);
	}
	
	this.getTile = function(i, j){
		return this.lines[i][j];
	}
}

function MapTile(i, j, map){
	this.i = i;
	this.j = j;
	this.attributesArray = {};
	this.getHTMLObject = function(){
		return document.getElementById('map').rows[this.i].cells[this.j];
	}
	
	this.setAttribute = function(attName, bool){
		this.attributesArray[attName] = bool;
		this.updateCSS();
	}
	
	this.updateCSS = function(){
		for(var key in this.attributesArray){
			this.getHTMLObject().setAttribute(key, this.attributesArray[key]);
		}
	}
	
	
	this.initialize = function(){
		var localI = this.i;
		var localJ = this.j;
		
		this.getHTMLObject().addEventListener(
			'mousedown',
			function(eventObj){
				//if(eventObj.target == eventObj.currentTarget){
					localMap.setMouseDownTile(localI, localJ);
					eventObj.preventDefault();
				//}
			},
			true
		);
		this.getHTMLObject().addEventListener(
			'mouseup',
			function(eventObj){
				//if(eventObj.target == eventObj.currentTarget){
					localMap.setMouseUpTile(localI, localJ);
					eventObj.preventDefault();
				//}
			},
			true
		);
	}
	
	this.commit = function(){
		gapi.hangout.data.setValue( 'TileUpdate:' + this.i + ':' + this.j, JSON.stringify(this) );
	}
	
	this.update = function(updateObject){
		for(var attribut in updateObject.attributesArray){
			this.setAttribute(attribut, updateObject.attributesArray[attribut]);
		}
	}
	
	this.setBackgroundImage = function(node){
		this.getHTMLObject().appendChild(node);
	}
	
	this.removeBackgroundImage = function(node){
		if(node != null)
			return this.getHTMLObject().removeChild(node);
	}
}
